#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <assert.h>
#include <string.h>
#include <ctype.h>

#include "halofinder.h"

#define LINELENGTH 1024

static int strcmp_caseinsens(const char *s1, const char *s2);

void read_config_file(char fname[])
{
  FILE *fp;
  char line[LINELENGTH];
  char *p,*tag,*value;
    
  //set some defaults
  haloFinderData.SnapshotHeaderList[0] = '\0';
  haloFinderData.PeakOutputFile[0] = '\0';
  haloFinderData.PeakInputFile[0] = '\0';
  haloFinderData.HaloOutputFile[0] = '\0';
  haloFinderData.HaloOverdens = -1.0;
  haloFinderData.PartMass = -1.0;
  haloFinderData.NumTasksIOInParallel = -1;
    
  //error check the file open
  fp = fopen(fname,"r");
  if(fp == NULL)
    {
      fprintf(stderr,"config file '%s' could not be opened!\n",fname);
      assert(fp != NULL);
    }
  
  while(fgets(line,LINELENGTH,fp) != NULL)
    {
      //convert all tabs and newlines to spaces
#ifdef DEBUG
#if DEBUG_LEVEL > 2
      if(ThisTask == 0)
	fprintf(stderr,"base line: '%s'\n",line);
#endif
#endif
      p = line;
      while(*p != '\0')
	{
	  if(*p == '\n' || *p == '\t')
	    *p = ' ';
	  ++p;
	}
      p = line;
#ifdef DEBUG
#if DEBUG_LEVEL > 2
      if(ThisTask == 0)
	fprintf(stderr,"conv line: '%s'\n",line);
#endif
#endif
      //go past any starting white space
      while(*p == ' ')
	++p;
      
      //if have reached end of string or string line is a comment, then go to next line
      if(*p == '\0' || *p == '#')
	continue;
      
      //now at start of tag - go to end and then null temrinate
      tag = p;
      while(*p != ' ')
	++p;
      *p = '\0';
      ++p;
#ifdef DEBUG
#if DEBUG_LEVEL > 2
      if(ThisTask == 0)
	fprintf(stderr,"tag: '%s'\n",tag);
#endif
#endif

      //skip more white space
      while(*p == ' ')
	++p;
      
      //get value
      value = p;
      while(*p != ' ' && *p != '#')
	++p;
      *p = '\0';
      ++p;
#ifdef DEBUG
#if DEBUG_LEVEL > 2
      if(ThisTask == 0)
	fprintf(stderr,"value: '%s'\n",value);
#endif
#endif

      //put tag value in struct
      if(strcmp_caseinsens(tag,"OmegaM") == 0)
	{
	  haloFinderData.OmegaM = (float) (atof(value));
	} 
      else if(strcmp_caseinsens(tag,"h") == 0)
	{
	  haloFinderData.h = (float) (atof(value));
	}
      else if(strcmp_caseinsens(tag,"totnumparts") == 0)
	{
	  haloFinderData.TotNumParts = atol(value);
	}
      else if(strcmp_caseinsens(tag,"scalefactorsnapshot") == 0)
	{
	  haloFinderData.ScaleFactorSnapshot = (float) (atof(value));
	}
      else if(strcmp_caseinsens(tag,"boxlengthsnapshot") == 0)
	{
	  haloFinderData.BoxLengthSnapshot = (float) (atof(value));
	}
      else if(strcmp_caseinsens(tag,"PartMass") == 0)
	{
	  haloFinderData.PartMass = (float) (atof(value));
	} 
      else if(strcmp_caseinsens(tag,"softeninglength") == 0)
	{
	  haloFinderData.SofteningLength = (float) (atof(value));
	}
      else if(strcmp_caseinsens(tag,"simulationtype") == 0)
	{
	  strcpy(haloFinderData.SimulationType,value);
	}
      else if(strcmp_caseinsens(tag,"halooutputfile") == 0)
	{
	  strcpy(haloFinderData.HaloOutputFile,value);
	}
      else if(strcmp_caseinsens(tag,"peakoutputfile") == 0)
	{
	  strcpy(haloFinderData.PeakOutputFile,value);
	}
      else if(strcmp_caseinsens(tag,"peakinputfile") == 0)
	{
	  strcpy(haloFinderData.PeakInputFile,value);
	}
      else if(strcmp_caseinsens(tag,"snapshotfilelist") == 0)
	{
	  strcpy(haloFinderData.SnapshotFileList,value);
	}
      else if(strcmp_caseinsens(tag,"snapshotheaderlist") == 0)
	{
	  strcpy(haloFinderData.SnapshotHeaderList,value);
	}
      else if(strcmp_caseinsens(tag,"NumTasksIOInParallel") == 0)
	{
	  haloFinderData.NumTasksIOInParallel = atoi(value);
	}
      else if(strcmp_caseinsens(tag,"domainbuffsize") == 0)
	{
	  haloFinderData.DomainBuffSize = (float) (atof(value));
	}
      else if(strcmp_caseinsens(tag,"halotruncradtype") == 0)
	{
	  strcpy(haloFinderData.HaloTruncRadType,value);
	}
      else if(strcmp_caseinsens(tag,"halooverdens") == 0)
	{
	  haloFinderData.HaloOverdens = (float) (atof(value));
	}
      else if(strcmp_caseinsens(tag,"MinimumPeakOverdensWRTMeanDens") == 0)
	{
	  haloFinderData.MinimumPeakOverdensWRTMeanDens = (float) (atof(value));
	}
      else if(strcmp_caseinsens(tag,"MinNumPartsHalo") == 0)
	{
	  haloFinderData.MinNumPartsHalo = (float) (atof(value));
	}
      else if(strcmp_caseinsens(tag,"PeakApertureSizeFactor") == 0)
	{
	  haloFinderData.PeakApertureSizeFactor = (float) (atof(value));
	}
      else if(strcmp_caseinsens(tag,"haloexcltype") == 0)
	{
	  strcpy(haloFinderData.HaloExclType,value);
	}
          else
	{
	  fprintf(stderr,"tag '%s' not found!\n",tag);
	  assert(0);
	}
    }
  
  fclose(fp);
  
#ifdef DSAMPLEFACTOR
  haloFinderData.TotNumParts = haloFinderData.TotNumParts/DSAMPLEFACTOR;
  haloFinderData.SofteningLength = (float) (haloFinderData.SofteningLength*pow((double) DSAMPLEFACTOR,1.0/3.0));
#endif
  
  /* error check some stuff */
  assert(strlen(haloFinderData.HaloOutputFile) != 0);
  
  if(strcmp(haloFinderData.HaloTruncRadType,"MEANOVERDENS") == 0 || strcmp(haloFinderData.HaloTruncRadType,"CRITOVERDENS") == 0)
    if(haloFinderData.HaloOverdens == -1.0)
      {
        fprintf(stderr,"need to set HaloOverDens in config file!\n");
        MPI_Abort(MPI_COMM_WORLD,123);
      }
  
  assert(haloFinderData.NumTasksIOInParallel > 0);
  assert(haloFinderData.NumTasksIOInParallel <= NTasks);
    
  //fill in partmass tag
#ifndef LIGHTCONE
  haloFinderData.PartMass = (float) (haloFinderData.OmegaM*RHO_CRIT*pow(haloFinderData.BoxLengthSnapshot,3.0)/((double) (haloFinderData.TotNumParts)));
#endif
#ifdef DEBUG
  if(ThisTask == 0)
    fprintf(stderr,"particle mass = %le Msolar/h\n",haloFinderData.PartMass);
#endif
  assert(haloFinderData.PartMass > 0.0);
  
  //set default parms
  if(NTasks > 1)
    haloFinderData.DomainBuffSize = 10.0;
  else
    haloFinderData.DomainBuffSize = 0.0;
#ifdef DSAMPLEFACTOR
  haloFinderData.MinNumPartsHalo = haloFinderData.MinNumPartsHalo/DSAMPLEFACTOR;
#endif
}

static int strcmp_caseinsens(const char *s1, const char *s2)
{
  int N,i,equal;
  
  if(strlen(s1) != strlen(s2))
    return 1;
  N = strlen(s1);

#ifdef DEBUG
#if DEBUG_LEVEL > 2  
  if(ThisTask == 0)
    fprintf(stderr,"s1 = '%s', s2 = '%s'\n",s1,s2);
#endif
#endif

  equal = 0;
  for(i=0;i<N;++i)
    {
#ifdef DEBUG
#if DEBUG_LEVEL > 2
      if(ThisTask == 0)
	fprintf(stderr,"s1[i] = '%c', s2[i] = '%c'\n",tolower(s1[i]),tolower(s2[i]));
#endif
#endif

      if(tolower(s1[i]) != tolower(s2[i]))
	{
	  equal = 1;
	  break;
	}
    }

#ifdef DEBUG
#if DEBUG_LEVEL > 2  
  if(ThisTask == 0)
    fprintf(stderr,"equal %d (0 is true, 1 is false, weird)\n",equal);
#endif
#endif

  return equal;
}

#undef LINELENGTH
